package IC.AST.SemanticAnalysis;

import IC.AST.*;
import IC.Error.ErrorStack;
import IC.Error.Error.ErrorType;
import IC.Error.ErrorStringIntervaled;
import IC.SymbolTables.Types.InstanceType;
import IC.SymbolTables.*;
import IC.SymbolTables.Types.VoidType;
import IC.SymbolTables.Types.NullType;
import IC.SymbolTables.Types.Symbol;
import IC.SymbolTables.Types.ArrayType;


//We are very sorry for the quality of the code in this class (don't get me wrong, it works.) but its ugly.
//please don't judge us buy this class alone. (its was very late) :)

class TrivialTypeVisitor extends Visitor 
{
	private SymbolTable _globalSymTable;
	public TrivialTypeVisitor(SymbolTable global) { _globalSymTable = global; }
	
	//crapy code... sorry
	private IC.SymbolTables.Types.BasicType.BasicTypeEnum ConvertHelper(BasicType.BasicTypeEnum p)
	{
		if (p.equals(BasicType.BasicTypeEnum.INT))
			return IC.SymbolTables.Types.BasicType.BasicTypeEnum.INT;
		else if (p.equals(BasicType.BasicTypeEnum.BOOL))
			return IC.SymbolTables.Types.BasicType.BasicTypeEnum.BOOL;
		else
			return IC.SymbolTables.Types.BasicType.BasicTypeEnum.STRING;
	}
	
	private IC.SymbolTables.Types.BasicType Convert(BasicType.BasicTypeEnum p)
	{
		return new IC.SymbolTables.Types.BasicType(ConvertHelper(p));
	}
	
	public Object Visit(BasicType ASTType)
	{
		if (ASTType.GetType() == null)
			ASTType.SetType(
					ArrayType.Dimensionlize(
							ASTType.GetDimension(),
									Convert(ASTType.GetBasicTypetype())));
		
		return null;
	}
	
	public Object Visit(ClassType ASTType)
	{
		if (ASTType.GetType() != null)
			return null;
		
		Symbol classSym;
		
		if((classSym = _globalSymTable.GetSymbol(ASTType.GetClassName(), false)) == null)
			return UnFoundClass(ASTType);
		
		ASTType.SetType(
				ArrayType.Dimensionlize(
						ASTType.GetDimension(), 
						(InstanceType)classSym.GetType()));
		return null;
	}
	
	private Object UnFoundClass(ClassType ASTType)
	{
		ErrorStack.Push(new ErrorStringIntervaled(ErrorType.Semantic, "Unknown Class. Used without been declared : " + ASTType.GetClassName(),
						ASTType.GetInterval()));
		ASTType.SetType(new IC.SymbolTables.Types.NullType());
	
		return null;
	}
	
	public Object Visit(NullLiteral ASTnode)
	{
		ASTnode.SetType(new NullType());
		return null;
	}
	
	public Object Visit(IntegerLiteral ASTnode)
	{
		ASTnode.SetType(new IC.SymbolTables.Types.BasicType(IC.SymbolTables.Types.BasicType.BasicTypeEnum.INT));
		return null;
	}
	
	public Object Visit(LengthExpression ASTnode)
	{
		ASTnode.SetType(new IC.SymbolTables.Types.BasicType(IC.SymbolTables.Types.BasicType.BasicTypeEnum.INT));
		return null;
	}
	
	public Object Visit(BooleanLiteral ASTnode)
	{
		ASTnode.SetType(new IC.SymbolTables.Types.BasicType(IC.SymbolTables.Types.BasicType.BasicTypeEnum.BOOL));
		return null;
	}
	
	public Object Visit(QuoteLiteral ASTnode)
	{
		ASTnode.SetType(new IC.SymbolTables.Types.BasicType(IC.SymbolTables.Types.BasicType.BasicTypeEnum.STRING));
		return null;
	}
	
	private Object VisitStatement(ASTNode node)
	{
		node.SetType(new VoidType());
		return null;
	}
	
	public Object Visit(Formal node)
	{
		node.GetASTType().Visit(this);
		node.SetType(node.GetASTType().GetType());
		return null;
	}
	
	public Object Visit(LiteralExpression node)
	{
		node.GetLiteral().Visit(this);
		node.SetType(node.GetLiteral().GetType());
		return null;
	}
	
	public Object Visit (ClassField node)
	{
		node.GetASTType().Visit(this);
		node.SetType(node.GetASTType().GetType());
		return null;
	}
	
	public Object Visit(FunctionCallStatement node) { return VisitStatement(node); }
	public Object Visit(DeclorationStatement node) { return VisitStatement(node); }
	public Object Visit(ContinueStatement node) { return VisitStatement(node); }
	public Object Visit(AssignStatement node) { return VisitStatement(node); }
	public Object Visit(ReturnStatement node) { return VisitStatement(node); }
	public Object Visit(BreakStatement node) { return VisitStatement(node); }
	public Object Visit(WhileStatement node) { return VisitStatement(node); }
	public Object Visit(ScopeStatement node) { return VisitStatement(node); }
	public Object Visit(IfStatement node) { return VisitStatement(node); }
}
